package com.balance.anole.client.retail.payment;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.balance.anole.business.to.ShopPaymentTO;
import com.balance.anole.client.retail.payment.exception.InvalidPaymentException;
import com.balance.anole.common.to.CodeLookTO;
import com.balance.anole.erp.security.SessionManager;
import com.balance.anole.utils.DateUtil;

public class PaymentHelper {

	public static Payment getProperPayment(ShopPaymentTO shopPayment){
		Payment result = getProperPayment(shopPayment.getPaymentMode());
		result.setPaid(shopPayment.getPaidValue());
		return result;
	}

	public static Payment getProperPayment(CodeLookTO paymentMode){
		String code = paymentMode.getCodeId();
		Payment result = null;
		if("C".equals(code)){
			result = new CashPayment(BigDecimal.ZERO, BigDecimal.ZERO);
		}else if("V".equals(code)){
			result = new CouponPayment(BigDecimal.ZERO, BigDecimal.ZERO);
		}else if("B".equals(code)){
			result = new BankPayment(BigDecimal.ZERO, BigDecimal.ZERO);
		}
		return result;
	}

	public static BigDecimal checkPaidValueAndGetChain(BigDecimal needPay, List<ShopPaymentTO> shopPayments){
		BigDecimal change = BigDecimal.ZERO;
		List<Payment> payments = simulatePay(needPay, shopPayments);

		Iterator<Payment> paymentIte = payments.iterator();
		while(paymentIte.hasNext()){
			Payment payment = paymentIte.next();
			change = payment.pay();
		}

		return change;
	}

	public static List<ShopPaymentTO> getShopPaymentsAfterCalculate(Boolean isReject, BigDecimal needPay, List<ShopPaymentTO> shopPayments){
		List<ShopPaymentTO> result = new ArrayList<ShopPaymentTO>();

		List<Payment> payments = simulatePay(needPay.abs(), shopPayments);

		Iterator<Payment> paymentIte = payments.iterator();
		while(paymentIte.hasNext()){
			Payment payment = paymentIte.next();
			ShopPaymentTO shopPaymentTO = new ShopPaymentTO();
			shopPaymentTO.setPaidValue(isReject ? payment.getRealPaidValue().negate() : payment.getRealPaidValue());
			shopPaymentTO.setPaymentMode(payment.getPaymentMode());
			shopPaymentTO.setCreateDt(DateUtil.getSystemCurrentDate());
			shopPaymentTO.setCreateBy(SessionManager.getInstance().getLoginUser().getStaffInfo());
			result.add(shopPaymentTO);
		}

		return result;
	}

	public static Set<ShopPaymentTO> getShopPaymentsAfterCalculate(Boolean isReject, BigDecimal needPay, Set<ShopPaymentTO> shopPayments){
		if(shopPayments != null){
			return new HashSet<ShopPaymentTO>(getShopPaymentsAfterCalculate(isReject, needPay, new ArrayList<ShopPaymentTO>(shopPayments)));
		}
		return null;
	}

	public static List<Payment> simulatePay(BigDecimal needPay, List<ShopPaymentTO> shopPayments){
		List<Payment> payments = new ArrayList<Payment>();
		if(shopPayments != null){
			Iterator<ShopPaymentTO> ite = shopPayments.iterator();
			while(ite.hasNext()){
				ShopPaymentTO shopPayment = ite.next();
				Payment payment = getProperPayment(shopPayment);
				payments.add(payment);
			}

			//sort payment by priority
			//coupon should be verified first.
			//cash should be verified at last.
			Collections.sort(payments, new PaymentPriorityComparator());

			Iterator<Payment> paymentIte = payments.iterator();
			BigDecimal leftPay = needPay;
			while(paymentIte.hasNext()){
				Payment payment = paymentIte.next();
				payment.setNeedPay(leftPay);
				if(! payment.isValid()){
					throw new InvalidPaymentException(payment.getPaymentValidationMsg());
				}
				leftPay = payment.pay().negate();
			}
		}
		return payments;
	}
}
